package com.kanshu.ksgb.fastread.doudou.common.net.retrofit;

import android.util.Log;
import android.util.LruCache;

import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;

import io.reactivex.schedulers.Schedulers;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.fastjson.FastJsonConverterFactory;

/**
 * retrofit基类
 * Created by wcy on 2017/4/20.
 */
public abstract class BaseRetrofit {

    private static final LruCache<String, Retrofit> retrofitCache = new LruCache<>(4);
    private static final LruCache<String, Object> serviceCache = new LruCache<>(10);
    private static final LruCache<String, BaseRetrofit> instanceCache = new LruCache<>(3);

    /**
     * 超时时间
     */
    private static final int DEFAULT_TIME_OUT = 12;
    private Retrofit mRetrofit;

    protected BaseRetrofit() {
        mRetrofit = getOrPut(retrofitCache, retrofitCacheKey(), () -> new Retrofit.Builder()
                .baseUrl(getBaseUrl())
                .addConverterFactory(FormObjConverterFactory.create())
                .addConverterFactory(FastJsonConverterFactory.create())
                .client(createOkHttpClient())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io()))
                .build());
    }


    public <T> T createService(Class<T> clazz) {
        return create(clazz);
    }

    @SuppressWarnings("unchecked")
    public <T> T create(Class<T> clazz) {
        return (T) getOrPut(serviceCache, serverCacheKey(clazz.getName()), () -> mRetrofit.create(clazz));
    }

    private OkHttpClient createOkHttpClient() {
        OkHttpClient.Builder builder = new OkHttpClient.Builder()
                .connectTimeout(provideTimeout(), TimeUnit.SECONDS)
                .readTimeout(provideTimeout(), TimeUnit.SECONDS)
                .writeTimeout(provideTimeout(), TimeUnit.SECONDS);
        builder.addInterceptor(new FormObjConverterFactory.ObjConverterInterceptor());
        builder.addInterceptor(new CommonRequestParamsInterceptor());
        builder.addInterceptor(new MyHttpLoggingInterceptor(message -> Log.d("http", message)).setLevel(HttpLoggingInterceptor.Level.BODY));
        return  wrapOkHttpBuilder(builder).build();
    }

    protected int provideTimeout() {
        return DEFAULT_TIME_OUT;
    }

    protected abstract String getBaseUrl();

    protected abstract OkHttpClient.Builder wrapOkHttpBuilder(OkHttpClient.Builder builder);

    private String retrofitCacheKey() {
        return this.getClass().getName() + getBaseUrl();
    }

    private String serverCacheKey(String clazzName) {
        return retrofitCacheKey() + "=>" + clazzName;
    }

    @SuppressWarnings("unchecked")
    protected synchronized static <T extends BaseRetrofit> T getInstance(Class<T> clazz, Callable<T> init) {
        String key = clazz.getName();
        return (T)getOrPut(instanceCache, key, init::call);
    }

    private static synchronized <T, R> R getOrPut(LruCache<T, R> source, T key, Callable<R> callable) {
        R r = source.get(key);
        if (r == null) {
            try {
                r = callable.call();
                source.put(key, r);
            } catch (Exception e) {
                throw new RuntimeException("init map value err", e);
            }
        }
        return r;
    }
}

